﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

namespace Zijian666
{
    /// <summary>
    /// 命令行执行器
    /// </summary>
    internal class CommandLineExecuter
    {
        // 解析命令行 Windows API
        [DllImport("shell32.dll", SetLastError = true)]
        private static extern IntPtr CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);

        // 解析命令行
        private static string[]? SplitArgs(string commandLine)
        {
            if (string.IsNullOrWhiteSpace(commandLine))
            {
                return null;
            }
            var argv = CommandLineToArgvW(commandLine, out var argc);

            // CommandLineToArgvW returns NULL upon failure.
            if (argv == IntPtr.Zero)
            {
                return null;
            }

            // Make sure the memory ptrToSplitArgs to is freed, even upon failure.
            try
            {
                var args = new string[argc];

                // ptrToSplitArgs is an array of pointers to null terminated Unicode strings.
                // Copy each of these strings into our split argument array.
                for (var i = 0; i < argc; i++)
                {
                    var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size);
                    args[i] = Marshal.PtrToStringUni(p)!;
                }

                return args;
            }
            finally
            {
                Marshal.FreeHGlobal(argv);
            }
        }

        // 执行对象
        private readonly object _executeObjcet;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <exception cref="ArgumentNullException">参数为null</exception>
        public CommandLineExecuter(object executeObjcet) => _executeObjcet = executeObjcet ?? throw new ArgumentNullException(nameof(executeObjcet));

        /// <summary>
        /// 执行命令行
        /// </summary>
        public string? Execute(string commandLine)
        {
            var args = SplitArgs(commandLine);
            if (args == null || args.Length == 0)
            {
                return null;
            }
            var methodName = args[0];
            if (methodName == "help")
            {
                // 默认处理 help 指令
                var helpList = new List<string>();
                var methods = _executeObjcet.GetType()
                                            .GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                                            .Select(method => (method, string.Join(" ", method.GetParameters().Select(p => "{" + p.ParameterType.Name.ToLowerInvariant() + "}"))));
                var space1 = methods.Max(x => x.method.Name.Length + 1);
                var space2 = methods.Max(x => space1 + 2 + x.Item2.Length + 1);
                foreach (var (method, param) in methods)
                {
                    var help = $"  {method.Name.ToLowerInvariant().PadRight(space1)}{param}";
                    var attr = method.GetCustomAttribute<DescriptionAttribute>();
                    if (attr != null)
                    {
                        help = help.PadRight(space2) + attr.Description;
                    }
                    helpList.Add(help);
                }
                helpList.Add("  help".PadRight(space2) + "查看帮助指令");
                return string.Join("\n", helpList);
            }
            else
            {
                var method = _executeObjcet.GetType().GetMethod(methodName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
                if (method == null)
                {
                    return "没有找到指令：" + methodName;
                }
                var parameters = method.GetParameters();
                var paramVaues = new object?[parameters.Length];
                for (var i = 0; i < parameters.Length; i++)
                {
                    var p = parameters[i];
                    var a = args.ElementAt(i + 1);
                    if (a == null)
                    {
                        if (!p.HasDefaultValue)
                        {
                            return $"指令：{method} 缺少参数";
                        }
                        paramVaues[i] = p.DefaultValue;
                    }
                    if (a == "@null")
                    {
                        paramVaues[i] = null;
                    }
                    else
                    {
                        paramVaues[i] = Convert.ChangeType(a, p.ParameterType);
                    }
                }
                var result = method.Invoke(_executeObjcet, paramVaues);
                if (result != null)
                {
                    return result.ToString();
                }
            }
            return null;
        }

    }
}
